; Disassembly of the file "Z:\home\knoppix\Public\CPM_KRYSS_Loader.bin" ; ; CPU Type: Z80 ; ; Using the opcode map file "Z:\home\knoppix\Public\CPM_KRYSS_Loader.opmap" ; ; Created with dZ80 2.0 ; ; on Friday, 13 of January 2017 at 09:52 PM ; ORG $FC00 ; code will start loaded at $0000 but will relocate at $FC00 DEFB $00 DI LD HL,STACK LD SP,HL ; stack at STACK LD A,$41 OUT ($FE),A ; write 41 to port C of 8255: ; ; set border to blue, set signal "SO" to 0, set signal "O5" to 0, ; ; set signal "O6" to 1 to allow access to video memory in the CP/M hw config LD HL,$0000 ; copy all of LD DE,$FC00 ; this code LD BC,$0200 ; from 0000 LDIR ; to FC00 and then... JP LOGO ; ...jump in copied code to logo routine ; ########## ATTENTION !!! ############ ; In order to determine the correct values for the next 4 words, we need to save the pass 2 assembler listing ; and see the actual addresses of CTC INT. ROUTINE #1 and CTC INT. ROUTINE #2 which must be copied here ; after the DEFW keywords, instead of the current values. ; ##################################### STACK DEFW $FC95 ; (FC18) CTC0 vector / CTC INT. ROUTINE #2 does nothing CTC_V1 DEFW $FC95 ; (FC1A) CTC1 vector / CTC INT. ROUTINE #2 does nothing CTC_V2 DEFW $FC88 ; (FC1C) CTC2 vector / CTC INT. ROUTINE #1 [replaces CTC0 vector with CTC1 vector] CTC_V3 DEFW $FC95 ; (FC1E) CTC3 vector / CTC INT. ROUTINE #2 [does nothing] FLAG DEFB $00 ; (FC20) used as flag: =00 right before & during track reading, =FF after T1STOR DEFB $00 ; (FC21) \ storage start address DEFB $00 ; (FC22) / for data read from disk (0000 initially) T2STOR DEFB $00 ; (FC23) \ storage start address for the second system track DEFB $28 ; (FC24) / (20 sectors * 512 bytes = 14h*200h = 2800h) @@@@@ $24 -> $28 @@@@@ modification for 10 sectors/track/side STOR DEFB $00 ; (FC25) \ current storage address DEFB $00 ; (FC26) / for data read from disk TCTC1 DEFB $00 ; (FC27) time constant for CTC channel 1 (256 bytes to read) TCTC2 DEFB $28 ; (FC28) time constant for CTC channel 2 ($28=2*$14 -> 2*$14*256=$14*512=20 sectors=1 track both sides) @@@@@ $24 -> $28 @@@@@ modification for 10 sectors/track/side SPEC DEFB $EF ; (FC29) \ these 2 bytes are arguments for a Specify command DEFB $FF ; (FC2A) / (SRT=E=2ms, HUT=F=240ms, HLT=7F=254ms, ND=1->Non-DMA mode) ARGS DEFB $00 ; (FC2B) arg#1: Side, Drive No. ARG1 DEFB $00 ; (FC2C) arg#2: C (Track no.) DEFB $00 ; (FC2D) arg#3: H (Head no.) DEFB $01 ; (FC2E) arg#4: R (Sector no.) DEFB $02 ; (FC2F) arg#5: N (Bytes/sector) *** 2 means 512 bytes/sector=128*2^N *** DEFB $0A ; (FC30) arg#6: EOT (Last sector no. of a track) *** 9 sectors/track *** @@@@@ $09 -> $0A @@@@@ modification for 10 sectors/track/side DEFB $2E ; (FC31) arg#7: GPL (Gap 3 Length, betw sectors) @@@@@ $50 -> $2E @@@@@ modification for 10 sectors/track/side DEFB $FF ; (FC32) arg#8: DTL (Data Length, no.of bytes to be rd/wr into sector) =FF if N!=0 DEFB $FF RSLT DEFB $00 ; (FC34) result byte#1: ST0 DEFB $00 ; (FC35) result byte#2: ST1 / PCN DEFB $00 ; (FC36) result byte#3: ST2 DEFB $00 ; (FC37) result byte#4: C DEFB $00 ; (FC38) result byte#5: H DEFB $00 ; (FC39) result byte#6: R DEFB $00 ; (FC3A) result byte#7: N DEFB $00 DEFB $00 START LD HL,STACK IM 2 LD A,H ; upper half of CTC interrupt table start address (FC)... LD I,A ; ...is stored in register I (see Z80 docs) LD A,L ; lower half of CTC interrupt table start address (18)... OUT ($E3),A ; ... is sent to CTC channel 0 ; MEANING: Interrupt Vector being used by all 4 channels!! ; Interrupt Vector = 00011cc0 where cc is the CTC channel. ; requesting the interrupt. So vector for CTC0 = 18, CTC1 = 1A, CTC2 = 1C, CTC3 = 1E ; The upper half of addr table pointer is FC thus we have the ; int. vectors at (FC18-FC19) for CTC0, (FC1A-FC1B) for CTC1 ; (FC1C-FC1D) for CTC2, (FC1E-FC1F) for CTC3 LD A,$FF ; data for CTC0 on next line: set CTC0 to counter mode and enable CTC0 interrupts OUT ($E3),A ; write FF to CTC channel 0 (Enable Interrupt, Counter Mode, ; Rising Edge, Time Const. Follows, Reset, Control). ; MEANING: Reset, Enable Interrupts for Channel 0, a Time Constant follows. LD A,$01 ; time constant for CTC0 on next line: generate INT for each byte transferred from 8272 to µP OUT ($E3),A ; write 01 to CTC channel 0 ; MEANING: Time Constant byte (=01, for 1 byte read). LD A,$7B ; data for CTC3 on next line: disable int, Counter Mode, rising edge, ; No Time Const. Follows, Software Reset OUT ($FB),A ; write 7B to CTC channel 3 (disable interrupt) LD HL,CTC2 ; CTC0 interrupt vector (for CTC INT. ROUTINE #2) to be set by CTC_1+2_INIT next CALL CTCINI ; call CTC_1+2_INIT CALL RD ; call 8272_RD (make sure 8272 is finished with any possible previous command) LD HL,SPEC ; start addr for arg bytes of a Specify command (0027 within this code) LD BC,$0303 ; B=03 for 3 command bytes, C=03 for 8272 Specify opcode CALL WR ; call 8272_WR (send a Specify command to 8272) L0066 EXX ; <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <---| LD BC,$0204 ; B=02 for 2 command bytes, C=04 for 8272 Sense Drive Status opcode | CALL WRHL ; call 8272_WR_HL (send a Sense Drive Status command to 8272, arg bytes from FC13) | CALL RD ; call 8272_RD (reads command result, returns ST3 in register A) | BIT 5,A ; test bit 5 of ST3 (RDY), should be 1(?) if READY | EXX ; | JR NZ,L007A ; if crt drive# is READY -->| | INC (HL) ; HL=FC13 points to first command arg (drive# and head#), this increments drive# | RES 2,(HL) ; set head# to 0 | | JR L0066 ; ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> --->| L007A CALL TRKRD ; call TRACK_READ <--- <-----| reads track 00 both sides CALL TRKRD ; call TRACK_READ reads track 01 both sides ; ############################ GET CPU IN SAME STATE AS LK.SYS BEFORE LEAVING ############################### ; LD A,$90 ; OUT ($E3),A ; EXX ; LD BC,$0102 ; standard CP/M Loader leaves BC'=$0102, and looks like standard CP/M needs it ; LD DE,$0002 ; standard CP/M Loader seems to leave DE=$??02 ... ; LD HL,$FCB2 ; standard CP/M Loader leaves HL'=$FCB2, and looks like standard CP/M needs it ; EXX ; DI ; ########################################################################################################### JP $0000 ; runs the code at the beginning of sector 1 track 00 side 0 (Boot Sector) CTC3 INI ; ######## CTC INT. ROUTINE #3 (FC8E) ####### EI ; this routine reads bytes from FDD RETI ; ######## CTC INT. ROUTINE #1 (FC93) ####### ; this is executed when CTC2 generates an interrupt, which ; happens after one full track has been read (both sides, ; 9 sectors each side, 18 sectors in total) CTC1 PUSH HL LD HL,(CTC_V1) ; \ overwrite current CTC0 int. vector in (FC00-FC01) LD (STACK),HL ; / with CTC1 int. vector from (FC02-FC03) LD HL,FLAG ; location FC08 used as a flag: LD (HL),$FF ; (FC08)=FF right after a track has been completely read POP HL CTC2 EI ; ######## CTC INT. ROUTINE #2 (FCA0) ####### RETI ; this int. routine does nothing ; ######## CTC_1+2_INIT (FC82) ######### ; inputs: HL=CTC0 interrupt vector CTCINI DI LD (STACK),HL ; set CTC0 interrupt vector to contents of HL LD A,$7F ; data for CTC1 on next line: disable int, Counter Mode, rising edge, ; Time Const. Follows, Software Reset OUT ($EB),A ; write 7F to CTC channel 1 LD A,(TCTC1) ; time constant for CTC channel 1 OUT ($EB),A ; write time constant to CTC channel 1 LD A,$FF ; data for CTC2 on next line: enable int, Counter Mode, rising edge, ; Time Const. Follows, Software Reset OUT ($F3),A ; write 7F to CTC channel 2 LD A,(TCTC2) ; time constant for CTC channel 2 OUT ($F3),A ; write time constant to CTC channel 2 RET ; ######## 8272_POLL (FC99) ######### POLL IN A,($F5) ; read 8272 Status Register | this routine polls the 8272 Status Register BIT 7,A ; test bit 7 (RQM) | until 8272 is ready to send or receive JR Z,POLL ; if bit 7 = 0 read 8272 Status Register again | data to or from the CPU. When ready (RQM=1) BIT 6,A ; test bit 6 | bit 6 is tested (DIO, 0=receive/1=send) RET ; ######## 8272_WR_HL (FCA2) ######### WRHL LD HL,ARGS ; routine below with arg bytes taken from FC13 ; ######## 8272_WR (FCA5) ######### ; send a command to 8272 ; inputs: HL=start addr of bytes to be sent (command args) ; C=first byte to be sent (command opcode) ; B=nr of command bytes (opcode+args) WR IN A,($F5) ; <--- <--- <--- <--- <-----| read 8272 Status Register BIT 4,A ; test bit 4 (FDC Busy) | JR NZ,WR ; ---> ---> ---> ---> ----->| repeat if FDC busy JR L00C5 ; ---> ---> --->| L00C3 LD C,(HL) ; <--- <--- <---- <--- <--- <---| INC HL ; | | L00C5 DI ; <--- <--- <---| | CALL POLL ; call 8272_POLL | CALL NZ,CPM2CO ; call CPM_TO_COBRA if 8272 to send data LD E,C ; | LD C,$FD ; | OUT (C),E ; send byte to 8272 Data Reg | DJNZ L00C3 ; ---> ---> ---> ---> ---> ---->| EI RET ; ######## 8272_SIS (FCBF) ######## SIS LD BC,$0108 ; B=01 for 1 command bytes, C=08 opcode for Sense Interrupt Status CALL WR ; call 8272_WR (send Sense Interrupt Status command to 8272) ; ######## 8272_RD (FCC5) ######### ; reads last command result bytes from 8272 ; outputs: A=first result byte ; (FC1C-FC22)=the max. 7 result bytes in order RD LD HL,RSLT ; start address to store 8272 command result bytes LD B,$08 ; counter (8 bytes to read from 8272) L00E0 CALL POLL ; call 8272_POLL <--- <--- <----| JR Z,L00F8 ; ---> ---> --->|if all result bytes are read, exit (normal exit) LD (HL),$FF ; | | IN A,($FD) ; read 8272 Data| Register | LD (HL),A ; store result | | INC HL ; increment storage pointer | DJNZ L00E0 ; ---> ---> --------> ---> ---->| CPM2CO DI ; ######## CPM_TO_COBRA (FCD7) ######## if 8 result bytes read and still reading, exit (error) XOR A ; | A=00 LD L,A ; | LD H,A ; | HL=0000 (jump address) OR $C1 ; | A=C1 OUT ($FE),A ; | set SO=1, O6=1, border=blue LD R,A ; | set bit 7 of R to 1 (change to startup config) JP (HL) ; | jump to 0000 in startup config L00F8 LD HL,RSLT ; <--- <--- <---| LD A,(HL) ; return with first result byte in A RET ; ######## TRACK_READ (FCE7) ######### ; reads one track both sides (9 sectors/side, 18 sectors in total) TRKRD LD BC,$030F ; B=03 for 3 command bytes, C=0F opcode for Seek command CALL WRHL ; call 8272_WR_HL (Seek command with args from FC13) L0103 CALL SIS ; call 8272_SIS (returns first result byte (ST0) in A) <--- <--- <--- <--| BIT 5,A ; test bit 5 of first result byte ST0 (Seek End bit, if 1 Seek ended ok) | JR Z,L0103 ; if Seek not ended, repeat SIS ---> ---> ---> ---> ---> ---> ---> --->| AND $50 ; test bits 6 and 4 (Abnorm. Termination/Equipment Check/Track00 not reached) CALL NZ,CPM2CO ; if any of above errors, CPM_TO_COBRA L010F CALL SIS ; call 8272_SIS (returns first result byte (ST0) in A) CP $80 ; check if invalid command (SIS after SIS should give ST0=80, invalid command) JR NZ,L010F ; if ST0 not 80, go back 2 lines (repeat SIS) L0116 IN A,($F5) ; read 8272 Status Register <---| AND $0F ; test bits 0-3 (FDD 0-3 Busy) | JR NZ,L0116 ; if any drive busy -> ---> -->| LD B,$03 ; counter for max. 3 loops to be executed next L011E EXX ; <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <----| LD BC,$024A ;B=02 for 2 command bytes, C=4A opcode for Read ID command in MFM mode | CALL WRHL ; call 8272_WR_HL (send Read ID command with arg from (FC13)) | CALL RD ; call 8272_RD (returns ST0 in A) | AND $C0 ; test bits 6, 7 (normal termination if both =0) | EXX ; | JR Z,L0132 ; --->| if normal termination skip next 2 lines | DJNZ L011E ; ------> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ->| CALL CPM2CO ; | CPM_TO_COBRA L0132 LD B,$0A ; <---| B as counter for max 10 loops next L0134 PUSH BC ; <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <-| XOR A ; location FC08 used as a flag: | LD (FLAG),A ; =00 right before and during track reading | LD HL,CTC3 ; CTC0 interrupt vector to be used next (for CTC INT. ROUTINE #3 - saves FDD bytes) | CALL CTCINI ; call CTC_1+2_INIT | LD BC,$09C6 ; B=09 for 9 command bytes, C=C6 opcode for Read Data (MT, MFM) on next line | CALL WRHL ; call 8272_WR_HL (send a Read Data (MT, MFM) to 8272) which leaves C=FD | LD HL,(T1STOR) ; HL=storage start addr for data read from disk (0000 to begin, 2400 after 1 track) | L0148 HALT ; wait for first interrupt from CTC <--- <--- <-| ## one interrupt per byte read, | IN A,($F5) ; read 8272 Main Status Register | ## bytes read by INI from (C)=(FD) | AND $20 ; test bit 5 (Non-DMA, 1 during exec in Non-DMA,| 0 when exec finished) | JR NZ,L0148 ; if exec not finished yet ---> ---> ---> ---->| | LD (STOR),HL ; save current storage address for data read from disk | LD HL,CTC2 ; CTC0 interrupt vector to be used next (for CTC INT. ROUTINE #2 - does nothing) | CALL CTCINI ; call CTC_1+2_INIT | CALL RD ; call 8272_RD (after this HL=FC1C) | LD A,(FLAG) ; location FC08 used as a flag for track reading | OR A ; | POP BC ; | JR Z,L0167 ; if track reading in progress skip 3 lines and restart loop --> ---> ---> ---> ---> --->| LD A,(HL) ; (HL)=(FC1C)=first result byte (ST0) of the 8272 Read Data command | AND $D8 ; test bits 7 6 4 3 (Interrupt Code, Equipment Check, Not Ready) | JR Z,L016C ; ---> ---> ---> ---> ---> ---> ---> ---> --->| if none of above errors | L0167 DJNZ L0134 ; ----> ---> ---> ---> ---> ---> ---> --->---> ---> ---> ---> ---> ---> ---> ---> ---->| CALL CPM2CO ; call CPM_TO_COBRA | L016C LD HL,ARG1 ; storage address for current track no. <-----| INC (HL) ; increment current track number LD HL,(T2STOR) ; copy the value of storage addr for second system track... LD (T1STOR),HL ; ...over the value of current storage addr RET LOGO LD HL,LOGODT LD DE,$5A00 ; video mem attrs address in CP/M LD B,$20 LOOP0 PUSH BC ; BC=$20xx <-- <--- <--- <--- <--- <--- <--- <--| LD C,(HL) ; C=($003B)=$00 | LD B,$08 ; 8 loops next | LOOP1 LD A,$00 ; <--- <--- <--- <--- <--- <--- <--- <--| | RLC C ; Carry Flag is 0 | | JR NC,LOOP2 ; jump if bit=0 --->| | | LD A,$49 ; color attr | | | <<< color attr for BRIGHT=1, INK=blue, PAPER=blue LOOP2 LD (DE),A ; <--- <--- <--- <---| ($5A00)=$00 | | INC DE ; DE=$5A01 | | DJNZ LOOP1 ; ---> ---> ---> ---> ---> ---> ---> -->| | POP BC ; BC=$20xx | INC HL ; HL=$003C | DJNZ LOOP0 ; ---> ---> ---> ---> ---> ---> ---> ---> ----->| 32 loops ($20) LD A,$01 OUT ($FE),A ; write 01 to port C of 8255 ; (set border to blue, set signal "SO" to 0, set signal "O5" to 0, ; set signal "O6" to 0 to allow access to DRAM#1 in the CP/M hw config) JP START ; ############## LOGO DATA START #################### ; this block is used to generate the logo while loading CP/M LOGODT DEFB $00 ; 5A00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 DEFB $00 ; 5A08: 00 00 00 00 00 00 00 00 DEFB $00 ; 5A10: 00 00 00 00 00 00 00 00 DEFB $00 ; 5A18: 00 00 00 00 00 00 00 00 DEFB $33 ; 5A20: 00 00 ## ## 00 00 ## ## 00 00 ## ## 00 00 ## ## ## 00 00 00 ## 00 ## 00 00 ## 00 00 00 ## 00 00 00 ## ## 00 00 ## 00 00 DEFB $8A ; 5A28: ## 00 00 00 ## 00 ## 00 DEFB $44 ; 5A30: 00 ## 00 00 00 ## 00 00 DEFB $64 ; 5A38: 00 ## ## 00 00 ## 00 00 DEFB $4A ; 5A40: 00 ## 00 00 ## 00 ## 00 00 ## 00 00 ## 00 ## 00 00 ## 00 00 ## 00 ## ## ## ## 00 00 ## 00 ## 00 ## 00 00 00 ## 00 ## 00 DEFB $4B ; 5A48: 00 ## 00 00 ## 00 ## ## DEFB $CA ; 5A50: ## ## 00 00 ## 00 ## 00 DEFB $8A ; 5A58: ## 00 00 00 ## 00 ## 00 DEFB $42 ; 5A60: 00 ## 00 00 00 00 ## 00 00 ## 00 00 00 00 ## 00 00 ## 00 ## 00 00 ## ## 00 ## 00 00 00 ## 00 00 ## ## 00 00 ## 00 ## 00 DEFB $53 ; 5A68: 00 ## 00 ## 00 00 ## ## DEFB $44 ; 5A70: 00 ## 00 00 00 ## 00 00 DEFB $CA ; 5A78: ## ## 00 00 ## 00 ## 00 DEFB $43 ; 5A80: 00 ## 00 00 00 00 ## ## 00 ## 00 00 00 00 ## ## ## 00 00 ## 00 00 ## 00 00 ## 00 00 ## 00 ## 00 ## 00 ## 00 ## 00 ## 00 DEFB $92 ; 5A88: ## 00 00 ## 00 00 ## 00 DEFB $4A ; 5A90: 00 ## 00 00 ## 00 ## 00 DEFB $AA ; 5A98: ## 00 ## 00 ## 00 ## 00 DEFB $4A ; 5AA0: 00 ## 00 00 ## 00 ## 00 00 ## 00 00 ## 00 ## 00 00 00 ## 00 00 00 ## 00 00 ## 00 00 ## 00 ## 00 ## 00 ## 00 ## 00 ## 00 DEFB $22 ; 5AA8: 00 00 ## 00 00 00 ## 00 DEFB $4A ; 5AB0: 00 ## 00 00 ## 00 ## 00 DEFB $AA ; 5AB8: ## 00 ## 00 ## 00 ## 00 DEFB $32 ; 5AC0: 00 00 ## ## 00 00 ## 00 00 00 ## ## 00 00 ## 00 00 00 ## 00 00 00 ## 00 00 ## 00 00 00 ## 00 00 00 ## 00 00 00 ## 00 00 DEFB $22 ; 5AC8: 00 00 ## 00 00 00 ## 00 DEFB $44 ; 5AD0: 00 ## 00 00 00 ## 00 00 DEFB $44 ; 5AD8: 00 ## 00 00 00 ## 00 00 DEFB $00 ; 5AE0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 DEFB $00 ; 5AE8: 00 00 00 00 00 00 00 00 DEFB $00 ; 5AF0: 00 00 00 00 00 00 00 00 DEFB $00 ; 5AF8: 00 00 00 00 00 00 00 00 ; ############## LOGO DATA END ######################